package com.yangrd.airship.generator.page.impl.factory;

import com.yangrd.airship.generator.page.*;
import com.yangrd.airship.generator.page.enhance.PageBaseColumnRegistry;
import com.yangrd.airship.generator.page.enhance.TranslateHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yangrd
 * @date 2021/11/8
 */
@RequiredArgsConstructor
@Slf4j
public class StrPageGeneratorDefineFactory implements PageGeneratorDefineFactory<StrPageGeneratorDefine> {

    private final PageBaseColumnRegistry baseColumnRegistry;

    private final TranslateHelper translateService;

    @Override
    public PageGeneratorDefine create(StrPageGeneratorDefine define) {
        return create(define.getName(), define.getCols(), define.getActions(), define.getQueryCriteriaSet());
    }

    public PageGeneratorDefine create(String name, Map<String, String> cols, Map<String, String> actions, Map<String, String> queryCriteriaSet) {
        Map<String, String> tempCols = new HashMap<>(cols);
        baseColumnRegistry.forEach((e) -> tempCols.put(e.getKey(), e.getValue()));
        List<PageColumn> tableColumns = mapList(tempCols, e -> convertTableColumn(e.getKey(), e.getValue()));
        List<PageAction> tableActions = mapList(actions, e -> convertTableAction(e.getKey(), e.getValue(), cols));
        List<PageQueryCriteria> queryCriteriaList = mapList(queryCriteriaSet, e -> convertTableQueryCriteria(e.getKey(), e.getValue()));
        return PageGeneratorDefine.of(
                name,
                queryCriteriaList,
                tableColumns,
                tableActions
        );
    }

    private PageColumn convertTableColumn(String key, String val) {
        String[] valArray = getStrings(val);
        Class<?> clazz = getClass(valArray[0]);

        Boolean show = valArray.length > 2 && (valArray[1] == null || Boolean.parseBoolean(valArray[1]));
        String title = valArray.length > 3 ? valArray[2] : translateService.translateCn(key);
        PageColumn.FetchType fetchType = Optional.ofNullable(valArray.length > 4 ? valArray[3] : null).map(o -> PageColumn.FetchType.valueOf(o.toUpperCase())).orElse(PageColumn.FetchType.SELF);
        return PageColumn.of(
                key,
                clazz,
                show,
                title,
                fetchType
        ).initMeta(val);
    }


    private PageAction convertTableAction(String key, String val, Map<String, String> cols) {
        PageAction.Action action = PageAction.Action.valueOf(key.toUpperCase());
        List<String> tableColumns;
        String tempHasAll = "*";
        if (val.contains(tempHasAll)) {
            tableColumns = new ArrayList<>(cols.keySet());
        } else {
            String[] valArray = getStrings(val);
            tableColumns = Stream.of(valArray).filter(Objects::nonNull).filter(cols::containsKey).collect(Collectors.toList());
        }
        return PageAction.of(
                action,
                tableColumns
        ).initMeta(val);
    }


    private PageQueryCriteria convertTableQueryCriteria(String key, String val) {
        String[] valArray = getStrings(val);
        PageQueryCriteria.CriteriaType criteriaType = PageQueryCriteria.CriteriaType.valueOf(valArray[0].toUpperCase());
        String valResource = valArray[1];
        return PageQueryCriteria.of(
                key,
                criteriaType,
                valResource
        ).initMeta(val);
    }

    private <T> List<T> mapList(Map<String, String> map, Function<Map.Entry<String, String>, T> mapper) {
        return map.entrySet().stream().map(mapper).collect(Collectors.toList());
    }

    private String[] getStrings(String val) {
        String[] vals = new String[]{val, null};
        String splitKey = ",";
        if (val.contains(splitKey)) {
            vals = val.split(splitKey);
        }
        return vals;
    }

    private Class<?> getClass(String className) {
        try {
            return Class.forName(className.contains(".") ? className : "java.lang." + className);
        } catch (ClassNotFoundException e) {
            log.warn("ClassNotFoundException : {}", e.getMessage());
            return String.class;
        }
    }


}
